package com.wsg.dep.buffer;

import com.wsg.dep.core.event.EventFactory;
import com.wsg.dep.demo.LongEvent;
import com.wsg.dep.seq.Sequencer;
import com.wsg.dep.util.UnSafeUtil;
import com.wsg.dep.util.Utils;
import sun.misc.Unsafe;

public class RingBufferFields<E> extends RingBufferPadding{
    /**
     * 用于存储缓冲区填充的大小。计算方式为128/操作系统每个元素所占的字节数，
     */
    private static final int BUFFER_PAD;
    /**
     * 表示存储元素的数组在内存中的基础偏移量。计算方式为：中间存储元素的开始偏移=数组本身的偏移+填充元素的偏移
     */
    private static final long REF_ARRAY_BASE;
    /**
     * 2代表32位系统，8代表64位系统
     * 用于计算元素在数组中的偏移量的位移值;
     */
    private static final int REF_ELEMENT_SHIFT;

    /**
     * 用于执行底层的内存操作
     */
    private static final Unsafe UNSAFE = UnSafeUtil.getUnsafe();


    static
    {
        //首先获取Object数组的索引缩放因子scale，根据scale的值（4或8，分别对应32位和64位系统）确定REF_ELEMENT_SHIFT的值
        final int scale = UNSAFE.arrayIndexScale(Object[].class);
        // 32位系统，每个元素占4个字节
        if (4 == scale)
        {
            REF_ELEMENT_SHIFT = 2;
        }
        // 64位系统，每个元素占8个字节
        else if (8 == scale)
        {
            REF_ELEMENT_SHIFT = 3;
        }
        else
        {
            throw new IllegalStateException("Unknown pointer size");
        }

        // entries前后各填充BUFFER_PAD个元素
        BUFFER_PAD = 128 / scale;

        // entries中真正存储元素的开始位置，由Object[]本身的偏移位置+前面填充的BUFFER_PAD个元素的偏移位置，此处的BUFFER_PAD << REF_ELEMENT_SHIFT相当于BUFFER_PAD*scale
        REF_ARRAY_BASE = UNSAFE.arrayBaseOffset(Object[].class) + (BUFFER_PAD << REF_ELEMENT_SHIFT);
    }

    /**
     * 用于计算元素在数组中的索引，通过 bufferSize - 1 计算得出，前提是 bufferSize 是 2 的幂次方。
     */
    private final long indexMask;

    /**
     * 用于存储环形缓冲区元素的数组，大小为 bufferSize + 2 * BUFFER_PAD，两端的填充部分用于减少伪共享。
     * 真正存储的数据索引开始位置为BUFFER_PAD
     */
    private final Object[] entries;

    /**
     * 环形缓冲区的大小。必须为2的幂次方！
     */
    protected final int bufferSize;

    /**
     * 与环形缓冲区相关的序列器，用于协调生产者和消费者的操作。
     * 此处仅使用其定义的BufferSize用于初始化环形缓冲区的大小
     */
    protected final Sequencer sequencer;

    /**
     * 构造器
     * @param eventFactory 事件工厂
     * @param sequencer 序列化器
     */
    public RingBufferFields(EventFactory<E> eventFactory, Sequencer sequencer) {
        this(eventFactory, sequencer, sequencer.getBufferSize());
    }

    /**
     * 构造器
     * @param eventFactory 事件工厂
     * @param sequencer 序列化器
     * @param bufferSize 存储的队列大小
     */
    protected RingBufferFields(EventFactory<E> eventFactory, Sequencer sequencer, int bufferSize) {
        this.sequencer = sequencer;
        this.bufferSize = bufferSize;

        Utils.bufferSizeCheck(bufferSize);

        this.indexMask = bufferSize - 1;

        // 初始化环形缓冲区
        this.entries = new Object[bufferSize + 2 * BUFFER_PAD];

        // 预分配事件
        fill(eventFactory);
    }

    /**
     * 预分配事件
     * @param eventFactory
     */
    private void fill(EventFactory<E> eventFactory)
    {
        // 事件实际填充在entries的中间，前后使用BUFFER_PAD个进行填充
        for (int i = 0; i < bufferSize; i++)
        {
            entries[BUFFER_PAD + i] = eventFactory.newInstance();
        }
    }

    /**
     * 根据给定的 sequence 值，计算元素在entries数组中的偏移量，并使用UNSAFE.getObject方法获取该位置的元素，然后将其强制转换为 E 类型返回。
     * @param sequence
     * @return
     */
    @SuppressWarnings("unchecked")
    protected final E elementAt(long sequence)
    {
        // 实现环形队列的关键在此，循环的定位元素位置
        // sequence & indexMask ：由于 bufferSize 是 2 的幂次方，indexMask = bufferSize - 1 可以将 sequence 映射到 [0, bufferSize - 1] 的范围内，实现循环索引
        return (E) UNSAFE.getObject(entries, REF_ARRAY_BASE + ((sequence & indexMask) << REF_ELEMENT_SHIFT));
    }


    public static void main(String[] args) {
        EventFactory eventFactory = new EventFactory<LongEvent>() {
            @Override
            public LongEvent newInstance() {
                return new LongEvent();
            }
        };
        RingBufferFields<LongEvent> longEventRingBufferFields = new RingBufferFields<LongEvent>(eventFactory, null, 8);
        for (int i = 0; i < 8; i++) {
            LongEvent longEvent = longEventRingBufferFields.elementAt(i);
            longEvent.set(i);
            System.out.println(longEvent);
        }
        System.out.println("赋值结束，读取开始");
        for (int i = 0; i < 16; i++) {
            LongEvent longEvent = longEventRingBufferFields.elementAt(i);
//            longEvent.set(i);
            System.out.println(longEvent);
        }
    }
}
